using System;
using System.Collections.Generic;
using System.Text;
using System.Globalization;

namespace GarbageTruck.WeekOfTheYear
{
    public class WeekOfTheYear : ITemporalExpression
    {

        #region CONSTRUCTORS

        public WeekOfTheYear()
        {
        }

        public WeekOfTheYear(string expresion)
        {
            string[] parameters = expresion.Split(',');

            this.Number = int.Parse(parameters[0]);
            if (parameters.Length > 1)
            {
                ignoreMonth = false;

                this.Month = int.Parse(parameters[1]);
            }
        }

        public WeekOfTheYear(int number)
        {
            this.Number = number;
        }

        public WeekOfTheYear(int number, int month)
        {
            this.Number = number;
            this.Month = Month;
            ignoreMonth = false;
        }

        #endregion CONSTRUCTORS

        #region FIELDS

        private int m_Number;
        private int m_Month;
        private bool ignoreMonth = true;
        private Calendar calendar = CultureInfo.CurrentCulture.Calendar;

        #endregion FIELDS

        #region PROPERTIES

        public int Number
        {
            get { return m_Number; }
            set { m_Number = value; }
        }

        public int Month
        {
            get { return m_Month; }
            set { m_Month = value; }
        }

        #endregion PROPERTIES

        #region METHODS

        public bool Contains(DateTime dateTime)
        {
            if (ignoreMonth)
            {
                if (Number > 0)
                {
                    return (Number == calendar.GetWeekOfYear(dateTime,
                        CultureInfo.CurrentCulture.DateTimeFormat.CalendarWeekRule,
                        CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek));
                }
                else
                {
                    int lastWeek = calendar.GetWeekOfYear(new DateTime(dateTime.Year, 12, 31),
                        CultureInfo.CurrentCulture.DateTimeFormat.CalendarWeekRule,
                        CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek);

                    return ((lastWeek + 1 + Number) == calendar.GetWeekOfYear(dateTime,
                        CultureInfo.CurrentCulture.DateTimeFormat.CalendarWeekRule,
                        CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek));
                }
            }
            else
            {
                DateTime start = new DateTime();
                DateTime end = new DateTime();
                if (Number > 0)
                {
                    start = new DateTime(dateTime.Year, this.Month, 1);
                }
                else
                {
                    start = new DateTime(dateTime.Year, this.Month, calendar.GetDaysInMonth(dateTime.Year, this.Month));
                }

                int value = ((int)CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek - (int)start.DayOfWeek);
                start = start.AddDays(value);
                end = start.AddDays(7);
                value = (Number < 0 ? -1 : 1);
                start = calendar.AddWeeks(start, Number - value);
                end = calendar.AddWeeks(end, Number - value);
                return (dateTime.Month == this.Month && dateTime >= start && dateTime <= end);
            }
        }

        public DateTime? GetNextOcurrence(DateTime dateTime)
        {
            DateTime currentYearOcurrence = new DateTime();
            DateTime nextYearOcurrence = new DateTime();
            DateTime firstDayOfTheWeek = new DateTime();
            int value = 0;

            if (ignoreMonth)
            {
                if (Number > 0)
                {
                    firstDayOfTheWeek = new DateTime(dateTime.Year, 1, 1);
                }
                else
                {
                    firstDayOfTheWeek = new DateTime(dateTime.Year, 12, 31);
                }

                value = (int)CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek - (int)firstDayOfTheWeek.DayOfWeek;
                firstDayOfTheWeek = firstDayOfTheWeek.AddDays(value);
                value = (Number < 0 ? -1 : 1);
                currentYearOcurrence = calendar.AddWeeks(firstDayOfTheWeek, Number - value);

                if (Number > 0)
                {
                    firstDayOfTheWeek = new DateTime(dateTime.Year + 1, 1, 1);
                }
                else
                {
                    firstDayOfTheWeek = new DateTime(dateTime.Year + 1, 12, 31);
                }
                value = (int)CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek - (int)firstDayOfTheWeek.DayOfWeek;
                firstDayOfTheWeek = firstDayOfTheWeek.AddDays(value);
                value = (Number < 0 ? -1 : 1);
                nextYearOcurrence = calendar.AddWeeks(firstDayOfTheWeek, Number - value);
            }
            else
            {
                if (Number > 0)
                {

                    firstDayOfTheWeek = new DateTime(dateTime.Year, Month, 1);

                }
                else
                {
                    firstDayOfTheWeek = new DateTime(dateTime.Year, Month, calendar.GetDaysInMonth(dateTime.Year, Month));
                }

                value = (int)CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek - (int)firstDayOfTheWeek.DayOfWeek;
                firstDayOfTheWeek = firstDayOfTheWeek.AddDays(value);
                value = (Number < 0 ? -1 : 1);
                currentYearOcurrence = calendar.AddWeeks(firstDayOfTheWeek, Number - value);

                if (Number > 0)
                {

                    firstDayOfTheWeek = new DateTime(dateTime.Year + 1, Month, 1);

                }
                else
                {
                    firstDayOfTheWeek = new DateTime(dateTime.Year + 1, Month, calendar.GetDaysInMonth(dateTime.Year, Month));
                }

                value = (int)CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek - (int)firstDayOfTheWeek.DayOfWeek;
                firstDayOfTheWeek = firstDayOfTheWeek.AddDays(value);
                value = (Number < 0 ? -1 : 1);
                nextYearOcurrence = calendar.AddWeeks(firstDayOfTheWeek, Number - value);
            }

            if (currentYearOcurrence.Year < dateTime.Year) currentYearOcurrence = currentYearOcurrence.AddDays(32 - currentYearOcurrence.Day);
            if (currentYearOcurrence.Year > dateTime.Year) currentYearOcurrence = currentYearOcurrence.AddDays(-currentYearOcurrence.Day);
            if (nextYearOcurrence.Year < dateTime.Year + 1) nextYearOcurrence = nextYearOcurrence.AddDays(32 - nextYearOcurrence.Day);
            if (nextYearOcurrence.Year > dateTime.Year + 1) nextYearOcurrence = nextYearOcurrence.AddDays(-nextYearOcurrence.Day);

            if (dateTime.Date < currentYearOcurrence)
            {
                return currentYearOcurrence;
            }
            else if (Contains(dateTime.Date.AddDays(1)))
            {
                return dateTime.Date.AddDays(1);
            }
            else
            {
                return nextYearOcurrence;
            }
        }

        public DateTime? GetEventEnd(DateTime dateTime)
        {
            return dateTime;
        }

        public override string ToString()
        {
            return string.Format(ignoreMonth ? "WOTY({0})" : "WOTY({0},{1})",
                this.Number,
                this.Month);
        }

        #endregion METHODS

    }
}
